package com.bestv.search.common.util;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.net.ftp.FTP;
import org.apache.commons.net.ftp.FTPClient;
import org.apache.commons.net.ftp.FTPFile;
import org.apache.commons.net.ftp.FTPReply;

import java.io.*;
import java.net.SocketException;

public class FtpUtil {
    protected final Log logger = LogFactory.getLog(getClass());
    private FTPClient ftpClient = new FTPClient();

    /**
     * Connect to ftp server.
     *
     * @param hostname
     * @param port
     * @param username
     * @param password
     * @param controlEncoding
     * @return
     */
    public boolean connect(String hostname, String username, String password, String controlEncoding) {
        try {
            ftpClient.connect(hostname);
            ftpClient.setControlEncoding(controlEncoding);
            ftpClient.setConnectTimeout(60000);
            ftpClient.enterLocalPassiveMode();
            // Set transfer type
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);

            // Login to server
            ftpClient.login(username, password);

            // Check connect status
            if (FTPReply.isPositiveCompletion(ftpClient.getReplyCode())) {
                return true;
            } else {
                logger.error("Login to FTP server " + hostname + " failed, please check user name and password.");
            }
        } catch (SocketException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return false;
    }

    /**
     * Disconnect ftp connect
     *
     * @throws IOException
     */
    public void disconnect() throws IOException {
        if (ftpClient.isConnected()) {
            ftpClient.disconnect();
        }
    }

    public boolean downloadFile(String remoteDir, String localDir, String fileName, String sysFileNameEncoding,
                                String appServerEncoding, boolean override) {
        File localFileDir = new File(localDir);
        if (!localFileDir.exists()) {
            logger.error("Local dir " + localDir + " doesn't exist, please create it first");
            return false;
        }

        if (!localFileDir.isDirectory()) {
            logger.error("Local dir " + localDir + " must be directory.");
            return false;
        }

        // check remoteDir whether is valid.
        try {
            ftpClient.changeWorkingDirectory(remoteDir);
        } catch (IOException e) {
            logger.error("Change remote dir " + localDir + " failed, please check it.");
            return false;
        }

        try {
            // Encoding file name
            fileName = new String(fileName.getBytes(sysFileNameEncoding), appServerEncoding);
            FTPFile[] files = ftpClient.listFiles(fileName);
            for (FTPFile ftpFile : files) {
                if (ftpFile.isFile()) {
                    String name = ftpFile.getName();
                    // Encoding file name
                    if (sysFileNameEncoding != null && appServerEncoding != null) {
                        name = new String(name.getBytes(appServerEncoding), sysFileNameEncoding);
                    }

                    // Check file name
                    if (name.equalsIgnoreCase(fileName)) {
                        OutputStreamWriter outWriter = null;
                        BufferedReader br = null;
                        try {
                            // Begin download
                            File localFile = new File(localFileDir, fileName);
                            long length = 0;
                            if (override) {
                                if (localFile.exists()) {
                                    localFile.delete();
                                }
                                localFile.createNewFile();
                            } else {
                                if (localFile.exists()) {
                                    length = localFile.length();
                                } else {
                                    localFile.createNewFile();
                                }
                            }

                            InputStream inStream = ftpClient.retrieveFileStream(name);
                            // 断点续传
                            if (!override) {
                                inStream.skip(length);
                            }
                            InputStreamReader inReader = new InputStreamReader(inStream, StringConst.UTF_8);
                            FileOutputStream outStream = new FileOutputStream(localFile, true);
                            outWriter = new OutputStreamWriter(outStream, StringConst.UTF_8);

                            char[] charBuffer = new char[1024];
                            int len = 0;
                            while ((len = inReader.read(charBuffer)) != -1) {
                                outWriter.write(charBuffer, 0, len);
                                outWriter.flush();
                            }

                        } catch (IOException e) {
                            logger.error("download file " + name + " failed from FTP server");
                            logger.error(e);
                            return false;
                        } finally {
                            try {
                                if (br != null) {
                                    br.close();
                                }
                                if (outWriter != null) {
                                    outWriter.close();
                                }
                            } catch (IOException e) {
                                logger.warn("Close stream failed.");
                                logger.warn(e);
                            }
                        }
                        break;
                    }
                }
            }
        } catch (Exception e) {
            logger.error("download file " + fileName + " failed from FTP server");
            logger.error(e);
            return false;
        }
        return true;
    }

    public boolean downloadFile(String remoteDir, String localDir, String fileName, boolean override) {
        return downloadFile(remoteDir, localDir, fileName, StringConst.UTF_8, StringConst.ISO_8859_1, override);
    }

    public boolean downloadDirectory(String remoteDir, String localDir, String sysFileNameEncoding,
                                     String appServerEncoding, boolean override) {
        File localFileDir = new File(localDir);
        if (!localFileDir.exists()) {
            logger.error("Local dir " + localDir + " doesn't exist, please create it first");
            return false;
        }

        if (!localFileDir.isDirectory()) {
            logger.error("Local dir " + localDir + " must be directory.");
            return false;
        }

        return downloadFilesByDir(remoteDir, localDir, sysFileNameEncoding, appServerEncoding, override);
    }

    private boolean downloadFilesByDir(String remoteDir, String localDir, String sysFileNameEncoding,
                                       String appServerEncoding, boolean override) {

        // check remoteDir whether is valid.
        try {
            ftpClient.changeWorkingDirectory(remoteDir);
        } catch (IOException e) {
            logger.error("Change remote dir " + localDir + " failed, please check it.");
            return false;
        }

        // Download files
        try {
            FTPFile[] files = ftpClient.listFiles();
            for (FTPFile ftpFile : files) {
                if (ftpFile.isFile()) {
                    // Is file
                    downloadFile(remoteDir, localDir, ftpFile.getName(), sysFileNameEncoding, appServerEncoding,
                            override);
                } else {
                    // Is Directory
                    downloadFilesByDir(remoteDir, localDir, sysFileNameEncoding, appServerEncoding, override);
                }
            }
        } catch (IOException e) {
            logger.error("Download file failed from dir " + remoteDir);
            logger.error(e);
            return false;
        }

        return true;
    }

    public boolean existFile(String remoteDir, String fileName, String sysFileNameEncoding, String appServerEncoding) {
        try {
            // Model
            ftpClient.enterLocalPassiveMode();
            // Set transfer type
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            ftpClient.changeWorkingDirectory(remoteDir);

            // Encoding file name
            fileName = new String(fileName.getBytes(sysFileNameEncoding), appServerEncoding);
            FTPFile[] files = ftpClient.listFiles(fileName);
            for (FTPFile file : files) {
                String name = file.getName();
                // Encoding file name
                name = new String(name.getBytes(appServerEncoding), sysFileNameEncoding);

                // Check file name
                if (name.equalsIgnoreCase(fileName)) {
                    return true;
                }
            }
        } catch (Exception e) {
            logger.error("download file " + fileName + " failed from FTP server");
            logger.error(e);
        }

        return false;
    }

    public boolean existFile(String remoteDir, String fileName) {
        return existFile(remoteDir, fileName, StringConst.UTF_8, StringConst.ISO_8859_1);
    }

    public FTPFile[] listFiles(String remoteDir) {
        try {
            // Model
            ftpClient.enterLocalPassiveMode();
            // Set transfer type
            ftpClient.setFileType(FTP.BINARY_FILE_TYPE);
            boolean success = ftpClient.changeWorkingDirectory(remoteDir);
            if (success) {
                // Encoding file name
                FTPFile[] files = ftpClient.listFiles();
                return files;
            } else {
                return null;
            }
        } catch (IOException e) {
            logger.error("List files failed from dir " + remoteDir);
            logger.error(e);
        }

        return null;
    }
}
